package sostats.core.query;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import sostats.core.help.SostatsException;

import com.google.common.collect.Lists;

public class Dataset {
	private final int dimension;
	private List<Entry> entries;
	public Dataset(int dimension){
		this.dimension = dimension;
		this.entries = new ArrayList<Dataset.Entry>();
	}
	public void addData(Object value, Object... keys) throws SostatsException{
		if(keys==null||keys.length!=dimension) 
			throw new SostatsException("the data's dimension does't match the dataset");
		Entry newItem = new Entry(value, keys);
		entries.add(newItem);
	}
	public List<Entry> getEntries(){
		return Lists.newArrayList(entries);
	}
	public int getDimension(){
		return dimension;
	}
	public void collect(final int index, DomainMapper mapper){
		HashMap<Entry,Object> res =new HashMap<Entry,Object>();
		for(Entry entry:entries){
			Entry newEntry = new Entry(null, entry.keys);
			newEntry.keys[index]= mapper.mapKey(newEntry.keys[index]);
			if(newEntry.keys[index]==null) continue;
			if(res.containsKey(newEntry)){
				res.put(newEntry, mapper.collect(res.get(newEntry), entry.value));
			}else{
				res.put(newEntry, entry.value);
			}
		}
		ArrayList<Entry> newList = new ArrayList<Entry>();
		for(Entry entry:res.keySet()){
			entry.value = res.get(entry);
			newList.add(entry);
		}
		entries = newList;
		final Comparator<Object> fieldComparator = mapper.getComparator();
		if(fieldComparator!=null){
			Collections.sort(entries, new Comparator<Entry>() {
				@Override
				public int compare(Entry o1, Entry o2) {
					return fieldComparator.compare(o1.getKeys()[index], o2.getKeys()[index]);
				}
			});
		}
	}
	public void sort(Comparator<Entry> comparator){
		Collections.sort(entries,comparator);
	}
	public static class Entry{
		Object value;
		Object[] keys;
		Entry(Object value, Object... keys){
			this.value = value;
			this.keys = Arrays.copyOf(keys, keys.length);
		}
		public Object getValue(){
			return value;
		}
		public Object[] getKeys(){
			return keys;
		}
		
		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + Arrays.hashCode(keys);
			result = prime * result + ((value == null) ? 0 : value.hashCode());
			return result;
		}
		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Entry other = (Entry) obj;
			if (!Arrays.equals(keys, other.keys))
				return false;
			if (value == null) {
				if (other.value != null)
					return false;
			} else if (!value.equals(other.value))
				return false;
			return true;
		}
		
	}
}
